package com.shijie.service.impl;

import com.shijie.constant.IdKeyPrefix;
import com.shijie.constant.SubmitExConstant;
import com.shijie.dto.JudgeDTO;
import com.shijie.dto.SubmitPutDTO;
import com.shijie.entity.Submit;
import com.shijie.exception.SubmitException;
import com.shijie.feign.JudgeFeign;
import com.shijie.mapper.SubmitMapper;
import com.shijie.result.SjResult;
import com.shijie.service.SubmitService;
import com.shijie.feign.PoolFeign;
import com.shijie.socket.CompleteSocket;
import com.shijie.utlis.JsonUtils;
import com.shijie.utlis.RedisIdWorker;
import com.shijie.vo.JudgePoolVO;
import com.shijie.vo.JudgeVO;
import com.shijie.vo.PoolVO;
import com.shijie.vo.SubmitVO;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SubmitServiceImpl implements SubmitService {

    private final SubmitMapper submitMapper;

    @Resource
    private final PoolFeign poolFeign;

    @Resource
    private final JudgeFeign judgeFeign;

    private final RedisIdWorker redisIdWorker;

    private final ExecutorService threadPool = Executors.newFixedThreadPool(10);

    @Override
    public Long put(SubmitPutDTO submitPutDTO) {
        long submitId = redisIdWorker.nextId(IdKeyPrefix.SUBMIT_KEY);
        Submit submit = Submit.builder()
                .submitId(submitId)
                .userId(submitPutDTO.getUserId())
                .poolId(submitPutDTO.getPoolId())
                .code(submitPutDTO.getCode())
                .codeType(submitPutDTO.getCodeType())
                .status(Submit.Status.Commited)
                .build();
        boolean ok = submitMapper.put(submit);

        if (ok) {
            threadPool.submit(() -> this.judgeSubmit(submitId, submitPutDTO));
            return submitId;
        } else {
            throw new SubmitException(SubmitExConstant.PUT_BUT_ERROR);
        }
    }

    @Override
    public List<SubmitVO> listDefault(Long userId) {
        List<Submit> submits = submitMapper.listDefault(userId);
        List<SubmitVO> submitVOs = new ArrayList<>();

        submits.forEach((submit) -> {
            PoolVO poolVO = poolFeign.getByPoolId(submit.getPoolId()).getData();
            SubmitVO submitVO = SubmitVO.builder()
                    .poolVO(poolVO)
                    .build();
            BeanUtils.copyProperties(submit, submitVO);

            submitVOs.add(submitVO);
        });
        log.info("submitVOs: {}", submitVOs);
        return submitVOs;
    }

    @Override
    public SubmitVO get(Long submitId) {
        Submit submit = submitMapper.get(submitId);
        PoolVO poolVO = poolFeign.getByPoolId(submit.getPoolId()).getData();
        SubmitVO submitVO = SubmitVO.builder()
                .poolVO(poolVO)
                .build();
        BeanUtils.copyProperties(submit, submitVO);
        return submitVO;
    }

    public void judgeSubmit(Long submitId, SubmitPutDTO submitPutDTO) {
        JudgePoolVO judgePoolVO = null;
        judgePoolVO = poolFeign.getJudgeRequire(submitPutDTO.getPoolId()).getData();
        // 获取失败操作
        if (judgePoolVO == null)
            throw new SubmitException(SubmitExConstant.PUT_NO_COMPILER);

        JudgeDTO judgeDTO = JudgeDTO.builder()
                .code(submitPutDTO.getCode())
                .codeType(submitPutDTO.getCodeType())
                .allType(judgePoolVO.getAllType())
                .casePath(judgePoolVO.getCasePath())
                .resultPath(judgePoolVO.getResultPath())
                .build();
        boolean ok = submitMapper.changeStatus(submitId, Submit.Status.Compiling);

        if (ok)
            threadPool.submit(() -> {
                SjResult<JudgeVO> rs = null;
                rs = judgeFeign.judge(judgeDTO);
                log.warn("judgeVO rs: {}", rs);
                if (null == rs) {
                    submitMapper.changeStatus(submitId, Submit.Status.Commited);
                } else if (rs.getCode() == 1) {
                    submitMapper.changeStatus(submitId, Submit.Status.Succeeded);
                    submitMapper.setSucceedInfo(submitId, rs.getData().getUseMilTime(), rs.getData().getUseKbMemory());
                } else {
                    submitMapper.changeStatus(submitId, Submit.Status.Failed);
                    submitMapper.setFailedMSg(submitId, rs.getMsg());
                }
                // socket通知
                CompleteSocket.sendMessage(submitId,
                        JsonUtils.toJson(null == rs ? SjResult.error("判题客户端异常！") : rs));
            });
        else
            throw new SubmitException("编译失败");
    }

}
